/*******************************************************************************
* Copyright (c) 2009 EclipseSource and others. All rights reserved. This
* program and the accompanying materials are made available under the terms of
* the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* EclipseSource - initial API and implementation
*******************************************************************************/
package org.eclipse.rap.ui.interactiondesign.layout;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.rap.ui.interactiondesign.layout.model.Layout;
import org.eclipse.rap.ui.interactiondesign.layout.model.LayoutSet;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.plugin.AbstractUIPlugin;
/**
* A <code>ElementBuilder</code> represents the super class for all custom
* builders. With a custom builder you can construct complex objects e.g. a
* header, which needs nine or less images and additional information about
* placing logos or other images.
* <p>
* To fit as much as possible patterns of web components we introduce this
* <code>ElementBuilder</code>. This concept use the builder design pattern.
* So, an instantiation can look like: <code>ElementBuilder builder = new
* HeaderBuilder(param, param)</code>. The benefit of this technique is, that
* you can work against a defined api and you can change the implementation
* easily.
* </p>
* <p>
* Every builder needs a <code>{@link LayoutSet}</code>, which can be
* contributed to the <code>org.eclipse.rap.ui.layouts</code>
* extension point.
* </p>
* The point is, every builder should regard, that a <code>LayoutSet</code>
* can define more or less images depending on the contribution in the
* extension. E.g. a header can hold nine images, but i also can hold just
* three. So, the builder have to look that the component is build correctly.
* </p>
*
* @since 1.2
*
* @see LayoutSet
*/
public abstract class ElementBuilder implements IAdaptable {
private Composite parent;
private Layout layout;
private LayoutSet layoutSet;
/**
* This constructor stores the parent composite and instantiate a
* <code>{@link LayoutSet}</code> for this instance. The <code>LayoutSet
* </code> can be used to create images, fonts, colors or postion data.
* This depends on what is defined for a specific builder.
* <p>
* Subclasses have to call this constructor because it register the object
* in the <code>{@link LayoutRegistry}</code>
* </p>
*
* @param parent the parent <code>{@link Composite}</code> for the component
* to build.
* @param layoutSetId the id of the <code>LayoutSet</code>, which belongs to
* this instance.
*
* @see LayoutSet
* @see LayoutRegistry#registerBuilder(ElementBuilder)
* @see Composite
*/
public ElementBuilder( final Composite parent, final String layoutSetId ) {
this.parent = parent;
LayoutRegistry registry = LayoutRegistry.getInstance();
String savedLayoutId = registry.getSavedLayoutId();
if( !savedLayoutId.equals( IPreferenceStore.STRING_DEFAULT_DEFAULT ) ) {
registry.setActiveLayout( savedLayoutId, false );
}
layout = registry.getActiveLayout();
if( layout != null ) {
layoutSet = layout.getLayoutSet( layoutSetId );
registry.registerBuilder( this );
} else {
String msg
= "no layout registered with default "
+ "id (LayoutRegistry.DEFAULT_LAYOUT_ID) or no layout activated "
+ "over branding extension.";
throw new IllegalArgumentException( msg );
}
}
/**
* Clients can call this method to add non standard components to the builder
* e.g. a logo placed on a label to show in a header.
* <p>
* So, subclasses can implement or ignore this method depending on their
* needs.
* </p>
* <p>
* Clients have to call this method before calling
* <code>{@link #build()}</code>.
* </p>
*
* @param control an instance of a <code>Cotrol</code> e.g. a <code>Composite
* </code> containing a image.
* @param layoutData can be any position information for the control.
* Usually it's a instance of <code>FormData</code>.
*
* @see FormData
* @see GridData
*/
public abstract void addControl(
final Control control, final Object layoutData );
/**
* This method do the same as <code>{@link #addControl(Control, Object)}
* </code>. The only difference is, that the position information can be
* loaded by the <code>{@link LayoutSet#getPosition(String)}</code>
* method.
*
* @param control an instance of a <code>Control</code> e.g. a <code>Composite
* </code> containing an image.
* @param positionId the unique id of a position data holding by the <code>
* LayoutSet</code> for this object.
*
* @see LayoutSet#getPosition(String)
* @see LayoutSet#addPosition(String, FormData)
*/
public abstract void addControl(
final Control control, final String positionId );
/**
* Subclasses can implement this method and process it. E.g. if a client
* want to add a logo for a header directly and not over the
* <code>{@link #addControl(Control, Object)}</code> method in a
* <code>Composite</code>.
*
* @param image an instance of a <code>Image</code> to add.
* @param layoutData can be any position information for the <code>Image</code>.
* Usually it's an instance of <code>FormData</code>.
*
* @see FormData
* @see GridData
*/
public abstract void addImage( final Image image, final Object layoutData );
/**
* This method do the same as <code>{@link #addImage(Image, Object)}</code>.
* The only difference is, that the position information can be
* loaded by the <code>{@link LayoutSet#getPosition(String)}</code>
* method.
*
* @param image an instance of an <code>Image</code> to add.
* @param positionId the unique id of a position data holding by the <code>
* LayoutSet</code> for this object.
*
* @see LayoutSet#getPosition(String)
* @see LayoutSet#addPosition(String, FormData)
*/
public abstract void addImage( final Image image, final String positionId );
/**
* This is the most important method in a builder. If a client call this, the
* builder have to build the needed component e.g. a header or footer
* regarding the defined <code>LayoutSet</code>.
*/
public abstract void build();
/**
* Subclasses can use this method to create an image by means of it's path in
* the <code>LayoutSet</code>.
* <p>Note that images can only be created for <code>LayoutSets</code> which
* were contributed via extensions.</p>
*
* @param path the path for the image to create.
* @return the created image.
*
* @see LayoutSet#addImagePath(String, String)
* @see LayoutSet#getImagePath(String)
*/
protected Image createImage( final String path ) {
Image result = null;
if( path != null ) {
String id = LayoutRegistry.getPluginIdForLayoutSet( layoutSet.getId() );
if( id != null ) {
ImageDescriptor descriptor
= AbstractUIPlugin.imageDescriptorFromPlugin( id, path );
result = descriptor.createImage();
}
}
return result;
}
/**
* Subclasses should dispose all created or rather added <code>Control</code>s
* and <code>Image</code>s in this method.
*/
public abstract void dispose();
/**
* Returns a <code>Color</code> object by it's id defined in the
* <code>LayoutSet</code>
*
* @param colorId the id of the color.
*
* @return the created <code>Color</code> object.
*
* @see LayoutSet#addColor(String, Color)
* @see LayoutSet#getColor(String)
*/
public Color getColor( final String colorId ) {
return layoutSet.getColor( colorId );
}
/**
* Returns a <code>FormData</code> object by it's id defines in the
* <code>LayoutSet</code>
*
* @param positionKey the key for the FormData
*
* @return the created <code>FormData</code> object.
*
* @see LayoutSet#addPosition(String, FormData)
* @see LayoutSet#getPosition(String)
*
* @since 1.3
*/
public FormData getPosition( final String positionKey ) {
return layoutSet.getPosition( positionKey );
}
/**
* Subclasses should implement this method to return the component,
* which is created during the <code>{@link #build()}</code> call.
*
* @return the <code>{@link Control}</code> created in the
* <code>{@link #build()}</code> method.
*/
public abstract Control getControl();
/**
* Returns a <code>Font</code> object by it's id defined in the
* <code>LayoutSet</code>
*
* @param fontID the id of the font.
*
* @return the created font object.
*
* @see LayoutSet#addFont(String, Font)
* @see LayoutSet#getFont(String)
*/
public Font getFont( final String fontID ) {
return layoutSet.getFont( fontID );
}
/**
* Return the image by its id in the <code>LayoutSet</code>. This method
* just extract the image path and call <code>{@link #createImage(String)}
* </code>.
*
* @param imageId the id of the image defined in the <code>LayoutSet</code>.
*
* @return the created image.
*
* @see LayoutSet#getImagePath(String)
* @see LayoutSet#addImagePath(String, String)
*/
public Image getImage( final String imageId ) {
Image result = null;
String imagePath = layoutSet.getImagePath( imageId );
if( imagePath != null ) {
result = createImage( imagePath );
}
return result;
}
/**
* This returns the <code>LayoutSet</code> for this builder. The layoutset
* contains all images, fonts, formdatas and colors, which are relevant for
* the whole builder layout.
* @return the <code>LayoutSet</code> object
*/
protected LayoutSet getLayoutSet() {
return layoutSet;
}
/**
* Returns the parent, which was set in <code>
* {@link #ElementBuilder(Composite, String)}</code>.
*
* @return the parent <code>Composite</code>.
*/
protected Composite getParent() {
return parent;
}
/**
* Should return the size from the representative component of the builder.
*
* @return the size as a Point. <code>Point.x</code> means the width and
* <code>Point.y</code> means the height of the component.
*/
public abstract Point getSize();
/**
* Subclasses may override this method to return more than one control. This
* is necessary sometimes to get more control over the builder.
*/
public Object getAdapter( final Class adapter ) {
return null;
}
}